'use strict';

const ipfsAPI = require('ipfs-http-client');
const BigNumber = require('bignumber.js').BigNumber;
const PDFDocument = require('pdfkit');
const sharp = require('sharp');
const qr = require('qr-image');
const path = require('path');
const fs = require('fs');
const moment = require('moment');
const got = require('got');
const stream = require('stream');
const crypto = require('crypto');
const chainsql = require('./chainsql');
const Joi = require('joi');
const _ = require('lodash');
const config = require('../configs');
const fromBuffer = require('pdf2pic').fromBuffer;
const FormData = require('form-data');
const { spawnSync } = require('child_process');

const ipfs = ipfsAPI(config.ipfs.url);

/**
 * sha256 签名算法
 * @param {String} finalStr - 需要签名的字符串
 */
function sha256(str, salt) {
  if (!salt) {
    salt = 'peersafe';
  }
  let hash = crypto
    .createHmac('sha256', salt)
    .update(str, 'utf8')
    .digest('hex');
  return hash;
}

/**
 * md5 文件hash
 * @param {Buffer}} buffer - 文件buffer
 */
function md5(buffer) {
  const hash = crypto.createHash('md5');
  hash.update(buffer, 'utf8');
  const md5 = hash.digest('hex');
  return md5;
}

/**
 * 生成验证码
 * @return {String} 验证码
 */
function createSMSCode() {
  let code = '';
  const codeLength = 6;
  const selectChar = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
  for (let i = 0; i < codeLength; i++) {
    const charIndex = Math.floor(Math.random() * 10);

    code += selectChar[charIndex];
  }
  return code;
}

/**
 * 获取请求对应ip
 * @param {Object} req 请求头内容
 * @return {String} ip地址
 */
function getClientIp(req) {
  return (
    req.headers['x-forwarded-for'] || // 判断是否有反向代理 IP
    req.connection.remoteAddress || // 判断 connection 的远程 IP
    req.socket.remoteAddress || // 判断后端的 socket 的 IP
    req.connection.socket.remoteAddress
  );
}
/**
 * 发送验证码
 * @param {String} str 发送内容
 * @return {String} 发送内容
 */
async function sendSMS(phone) {
  var codeStr = createSMSCode();
  const codeJson = JSON.stringify({
    code: codeStr
  });
  const params = {
    PhoneNumbers: phone,
    SignName: config.sms.signName,
    TemplateCode: config.sms.templateCode.loginOrRegister,
    TemplateParam: codeJson,
    RegionId: 'cn-shanghai'
  };
  const requestOption = {
    method: 'POST'
  };
  const result = await smsClient.request('SendSms', params, requestOption);
  if (result.Code == 'OK') {
    return codeStr;
  } else {
    throw new Error(result.Code);
  }
}

//上传文件到ipfs
async function uploadFile(path) {
  const stream = fs.createReadStream(path);
  var res = await ipfs.add(stream);
  return res.path;
}

//下载文件
async function downloadFile(filePath, savePath) {
  const stream = ipfs.cat(filePath);
  const ws = fs.createWriteStream(savePath);
  for await (const chunk of stream) {
    ws.write(chunk);
  }
}

//10进制转52
function string10to52(str) {
  var chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ';
  var radix = chars.length;
  var arr = [];
  var qutient = BigNumber(str);
  while (qutient > 0) {
    var mod = qutient % radix;
    arr.push(chars[mod]);
    qutient = (qutient - mod) / radix;
    if (qutient < radix) {
      arr.push(chars[qutient]);
      qutient = 0;
    }
  }
  return arr.join('');
}

//旋转照片
async function rotateImage(path, direction) {
  var { info } = await sharp(path).raw().toBuffer({ resolveWithObject: true });
  if (direction == 'down') {
    var outputBuffer = await sharp(path)
      .rotate(180)
      .resize(info.width, info.height)
      .toBuffer();
    fs.writeFileSync(path, outputBuffer);
  } else if (direction == 'right' || direction == 'left') {
    var outputBuffer = await sharp(path)
      .rotate(direction == 'right' ? 90 : -90)
      .resize(info.height, info.width)
      .toBuffer();
    fs.writeFileSync(path, outputBuffer);
    return { width: info.height, height: info.width };
  }
  return info;
}

async function generatePDF(info, imagePath) {
  var imageInfo = await rotateImage(imagePath, info.direction);
  return new Promise((resolve, reject) => {
    try {
      const doc = new PDFDocument();
      var data = [];
      doc.on('data', (chunk) => {
        data.push(chunk);
      });

      //注册字体
      doc.registerFont(
        'Heavy',
        path.join(__dirname, '../static/fonts/Alibaba-PuHuiTi-Heavy.ttf')
      );
      doc.registerFont(
        'Medium',
        path.join(__dirname, '../static/fonts/Alibaba-PuHuiTi-Medium.ttf')
      );
      var height = (imageInfo.height / imageInfo.width) * 550;
      doc.image(imagePath, 32, 30, {
        width: 550,
        height: height
      });

      if (info.text) {
        var startX = 100;
        var startY = 280;
        var fontSize = 35;
        if (info.text.length <= 50) {
          startY += (5 - Math.round(info.text.length / 10)) * 15;
        } else {
          fontSize = 28;
          startY = 200;
          startY += (9 - Math.round(info.text.length / 11)) * 15;
        }

        doc
          .fontSize(fontSize)
          .fillColor('#FFFFFF')
          .font(path.join(__dirname, '../static/fonts/方正仿宋简体_猫啃网.TTF'))
          .strokeColor('#FFFFFF')
          .text(info.text, startX, startY, {
            width: 430,
            height: 600,
            align: 'justify',
            stroke: true,
            baseline: 'bottom',
            lineGap: 10,
            characterSpacing: 8
          })
          .fillColor('#FFFFFF');
      }

      doc.image(path.join(__dirname, '../static/b.png'), 15, 15, {
        width: 112,
        height: 100
      });

      doc.image(path.join(__dirname, '../static/c.png'), 32, height - 20, {
        width: 550,
        height: 50
      });

      doc
        .fontSize(18)
        .fillColor('#FFFFFF')
        .font(
          path.join(__dirname, '../static/fonts/Alibaba-PuHuiTi-Medium.ttf')
        )
        .text(moment(info.time).format('YYYY/MM/DD  HH:mm:ss'), 70, height + 5);

      doc.image(path.join(__dirname, '../static/a.png'), 80, height + 45, {
        width: 300,
        height: 80
      });

      doc.image(info.qrPath, 476, height + 35, {
        width: 106,
        height: 100
      });

      doc.end();

      doc.on('end', async function () {
        var path = await pdf2image(data, info.uuid, info.direction);
        resolve(path);
      });
    } catch (error) {
      console.log(error);
      reject();
    }
  });
}

/**
 * 生成带logo的二维码
 * @param {String}} content - 二维码内容
 * @param {String}} logoPath - logo路径
 * @param {String}} path - 生成文件路径
 */
async function generateQR(content, logoPath, path) {
  var qrImag = qr.imageSync(content, { type: 'png', ec_level: 'H' });

  var sharpQR = await sharp(qrImag);
  var qrInfo = await sharpQR.metadata();
  var logoHeight = Math.ceil(qrInfo.height / 5);
  var logoWidth = Math.ceil(qrInfo.width / 5);

  var sharpLogo = await sharp(logoPath)
    .resize(logoWidth, logoHeight, { quality: 500 })
    .toBuffer();
  await sharpQR.composite([{ input: sharpLogo }]).toFile(path);
}

async function pdf2image(data, uuid, direction) {
  var buf = Buffer.concat(data);
  const options = {
    density: 100,
    saveFilename: `${uuid}_pdf`,
    savePath: path.join(config.rootPath, '/cache'),
    format: 'png',
    width: 612,
    height: 792
  };
  var storeAsImage = fromBuffer(buf, options);
  await storeAsImage();
  var sourcePath = path.join(config.rootPath, `/cache/${uuid}_pdf.1.png`);
  if (direction == 'right' || direction == 'left') {
    //如果是横屏拍摄,需要裁剪图片
    var targetPath = path.join(config.rootPath, `/cache/${uuid}_pdf.2.png`);
    await sharp(sourcePath)
      .extract({ top: 0, left: 0, width: 612, height: 620 })
      .resize(612 * 3, 620 * 3, { quality: 100 })
      .sharpen()
      .toFile(targetPath);
    spawnSync('mv', [targetPath, sourcePath]);
  }
  return sourcePath;
}

/**
 * @param {Number} s 毫秒数
 */
function sleep(s) {
  return new Promise(function (reslove, reject) {
    setTimeout(function () {
      reslove();
    }, s);
  });
}

//生成6位随机字符串
function generateRandomCode(len) {
  if (!len) {
    len = 6;
  }
  let chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
  /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
  let maxPos = chars.length;
  var code = '';
  for (let i = 0; i < len; i++) {
    code += chars.charAt(Math.floor(Math.random() * maxPos));
  }
  return code;
}

function createFolder(path) {
  try {
    fs.mkdirSync(path);
  } catch (error) {}
}

function isExist(path) {
  try {
    fs.accessSync(path);
    return true;
  } catch (error) {
    return false;
  }
}

/**
 * @param {String} scene 二维码内容
 * @param {String} path 二维码存储路径
 */
async function getWxCode(scene, path) {
  var res = await got.get(`${config.league.url}/public/wxtoken`, {
    searchParams: {
      key: config.league.key
    },
    responseType: 'json'
  });
  var url = `${config.wx.url}/wxa/getwxacodeunlimit?access_token=${res.body.data.token}`;
  res = got.stream.post(url, {
    json: {
      scene: scene,
      page: 'pages/evidence/verification/upload/index',
      auto_color: true,
      width: 1280
    },
    https: { rejectUnauthorized: false }
  });
  await pipeline(res, fs.createWriteStream(path));
}

/**
 * @param {String} imagePath 图片路径
 */
async function wxImageCheck(imagePath) {
  var res = await got.get(`${config.league.url}/public/wxtoken`, {
    searchParams: {
      key: config.league.key
    },
    responseType: 'json'
  });
  const formdata = new FormData();
  formdata.append('meida', fs.createReadStream(imagePath));
  var url = `${config.wx.url}/wxa/img_sec_check?access_token=${res.body.data.token}`;
  res = await got
    .post(url, {
      body: formdata,
      https: { rejectUnauthorized: false }
    })
    .json();
  return res;
}
/**
 * @param {String} from 输入流
 * @param {String} to 输出流
 */
async function pipeline(from, to) {
  return new Promise((resolve, reject) => {
    stream.pipeline(from, to, function (err) {
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
}

/**
 * @param {String} sourcePath 原文件路径
 * @param {String} targetPath 缩略图文件路径
 */
async function thumbnail(sourcePath, targetPath) {
  var { info } = await sharp(sourcePath)
    .raw()
    .toBuffer({ resolveWithObject: true });
  var outputBuffer = await sharp(sourcePath)
    .resize(parseInt(info.width / 4), parseInt(info.height / 4), {
      quality: 100
    })
    .toBuffer();
  fs.writeFileSync(targetPath, outputBuffer);
}

function cipher(str, key) {
  const iv = 'fedcba9876543210';
  try {
    const cipher = crypto.createCipheriv('aes-128-cbc', key, iv);
    return cipher.update(str, 'utf8', 'hex') + cipher.final('hex');
  } catch (err) {
    console.log('加密失败');
    return err.message || err;
  }
}

function decipher(str, key) {
  const iv = 'fedcba9876543210';
  try {
    const decipher = crypto.createDecipheriv('aes-128-cbc', key, iv);
    return decipher.update(str, 'hex', 'utf8') + decipher.final('utf8');
  } catch (err) {
    console.log('解密失败');
    return err.message || err;
  }
}

exports = module.exports = {
  schema,
  sendSMS,
  getClientIp,
  string10to52,
  generatePDF,
  uploadFile,
  downloadFile,
  sha256,
  md5,
  sleep,
  generateRandomCode,
  createFolder,
  rotateImage,
  generateQR,
  isExist,
  getWxCode,
  pipeline,
  thumbnail,
  wxImageCheck,
  cipher,
  decipher
};
